﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Text;
using MapControl.Misc;
using MapWinGIS;
using Point = System.Drawing.Point;

namespace MapControl
{
    public class GPSPoint
    {


        public string Name;
        public DateTime LocalTime;
        public double LonGPS;
        public double LatGPS;
        public double Alt;
        public double Speed;
        public double Angle;

        public PointF CenterPoint;




        private bool showRoute = false;
        private AxMapWinGIS.AxMap AxMap1;
        //Hình chữ nhật 1
        private RectangleF _rect;
        private const float r = 0.02f;
        //Hình chữ nhật 2
        //private const float r2 = 0.05f;

        private bool selected;
        /// <summary>
        /// Selection flag
        /// </summary>
        public bool Selected
        {
            get
            {
                return selected;
            }
            set
            {
                selected = value;
            }
        }

        /// <summary>
        /// Get number of handles
        /// </summary>
        public int HandleCount
        {
            get
            {
                return 8;
            }
        }

        /// <summary>
        /// Get number of handles
        /// </summary>
        public bool ShowRoute
        {
            get { return showRoute; }
            set { showRoute = value; }
        }

        public GPSPoint()
        {

        }

        public GPSPoint(AxMapWinGIS.AxMap map)
        {
            AxMap1 = map;
            InitRect2();
        }

        public GPSPoint(AxMapWinGIS.AxMap map, string name, DateTime local, double lon, double lat, double alt, double speed, double angle)
        {
            AxMap1 = map;
            LocalTime = local;
            Name = name;
            LonGPS = lon;
            LatGPS = lat;
            Alt = alt;
            Speed = speed;
            Angle = angle;

            InitRect2();
        }

        public void InitRect()
        {
            double x1, y1, x2, y2, centerX, centerY;
            x1 = y1 = x2 = y2 = centerX = centerY = 0;
            double lon1, lat1, lon2, lat2;
            lon1 = LonGPS - r;
            lat1 = LatGPS + 3 * r;
            lon2 = LonGPS + r;
            lat2 = LatGPS - 3 * r;

            AxMap1.ProjToPixel(lon1, lat1, ref x1, ref y1);
            AxMap1.ProjToPixel(lon2, lat2, ref x2, ref y2);
            _rect = new RectangleF((float)x1, (float)y1, (float)Math.Abs(x2 - x1), (float)Math.Abs(y2 - y1));
            AxMap1.ProjToPixel(lon1, lat1, ref x1, ref y1);
            AxMap1.ProjToPixel(LonGPS, LatGPS, ref centerX, ref centerY);

            CenterPoint = new PointF((float)centerX, (float)centerY);
        }

        /// <summary>
        /// Tính khoảng cách giữa 2 điểm theo km
        /// </summary>
        /// <param name="pointTo"></param>
        /// <returns></returns>
        public double Distance(GPSPoint pointTo)
        {
            double MilesY = Math.Abs(pointTo.LatGPS - LatGPS)*69.172;
             //The distance between lines of longitude changes as you go further north or south
            double MilesX = Math.Abs(pointTo.LonGPS - LonGPS)*69.172*Math.Cos(pointTo.LatGPS*Math.PI/180);
            double Dist = Math.Sqrt(Math.Pow(MilesX, 2) + Math.Pow(MilesY , 2))/1000;
            return Dist;
        }

        public void InitRect2()
        {
            double x1, y1, x2, y2, centerX, centerY;
            x1 = y1 = x2 = y2 = centerX = centerY = 0;
            double lon1, lat1, lon2, lat2;
            lon1 = LonGPS - r;
            lat1 = LatGPS + r;
            lon2 = LonGPS + r;
            lat2 = LatGPS - r;

            AxMap1.ProjToPixel(lon1, lat1, ref x1, ref y1);
            AxMap1.ProjToPixel(lon2, lat2, ref x2, ref y2);
            _rect = new RectangleF((float)x1, (float)y1, (float)Math.Abs(x2 - x1), (float)Math.Abs(y2 - y1));
            AxMap1.ProjToPixel(lon1, lat1, ref x1, ref y1);
            AxMap1.ProjToPixel(LonGPS, LatGPS, ref centerX, ref centerY);

            CenterPoint = new PointF((float)centerX, (float)centerY);
        }


        public PointF GetPoint()
        {
            double x, y;
            x = y = 0;
            AxMap1.ProjToPixel(LonGPS, LatGPS, ref x, ref y);
            return new PointF((float)x, (float)y);
        }

        private void DrawArrow2(Graphics g, Color color)
        {
            Pen pen = new Pen(color, 1);
            SolidBrush brush = new SolidBrush(color);
            InitRect();

            PointF[] pArray1 = new PointF[4];
            pArray1[0] = new PointF(_rect.X + _rect.Width * (4f / 10), _rect.Y);
            pArray1[1] = new PointF(_rect.X + _rect.Width * (6f / 10), _rect.Y);
            pArray1[2] = new PointF(_rect.X + _rect.Width * (6f / 10), _rect.Y + _rect.Height * (3f / 4));
            pArray1[3] = new PointF(_rect.X + _rect.Width * (4f / 10), _rect.Y + _rect.Height * (3f / 4));

            PointF[] pArray2 = new PointF[4];
            pArray2[0] = new PointF(_rect.X, _rect.Y + _rect.Height * (3f / 4));
            pArray2[1] = new PointF(_rect.X + _rect.Width / 2f, _rect.Y + _rect.Height / 2f);
            pArray2[2] = new PointF(_rect.X + _rect.Width, _rect.Y + _rect.Height * (3f / 4));
            pArray2[3] = new PointF(_rect.X + _rect.Width / 2f, _rect.Y + _rect.Height);

            // Set world transform of graphics object to translate.
            Matrix myMatrix = new Matrix();
            myMatrix.RotateAt((float)Angle, CenterPoint);

            myMatrix.TransformPoints(pArray1);
            g.FillPolygon(brush, pArray1);
            myMatrix.TransformPoints(pArray2);
            g.FillPolygon(brush, pArray2);

            pen.Dispose();
            brush.Dispose();
        }

        private void DrawCircle(Graphics g, Color color)
        {
            Pen pen = new Pen(color, 2);

            InitRect2();

            SolidBrush brush = new SolidBrush(color);
            g.FillEllipse(brush, _rect);

            pen.Dispose();
        }

        private Bitmap RotateImage(Bitmap bmp, float angle)
        {
            Bitmap rotatedImage = new Bitmap(bmp.Width, bmp.Height);
            using (Graphics g = Graphics.FromImage(rotatedImage))
            {
                g.TranslateTransform(bmp.Width / 2.0f, bmp.Height / 2.0f); //set the rotation point as the center into the matrix
                g.RotateTransform(angle); //rotate
                g.TranslateTransform(-bmp.Width / 2.0f, -bmp.Height / 2.0f); //restore rotation point into the matrix
                g.DrawImage(bmp, new Point(0, 0)); //draw the image on the new bitmap
            }

            return rotatedImage;
        }

        private void DrawImage(Graphics g, Bitmap curBitmap)
        {
            InitRect2();
            g.InterpolationMode = InterpolationMode.HighQualityBicubic;
            curBitmap = RotateImage(curBitmap, (float)Angle);
            g.DrawImage(curBitmap, _rect);
            // Dispose of objects
            curBitmap.Dispose();
        }

        private void DrawCircleImage(Graphics g)
        {
            InitRect2();
            Bitmap curBitmap = new Bitmap(@"circle.png");
            g.DrawImage(curBitmap, _rect);
            curBitmap.Dispose();
        }

        private void DrawPlaneImage(Graphics g)
        {
            //Bitmap curBitmap = new Bitmap(@"heli.png");
            Bitmap curBitmap = new Bitmap(@"plane.png");
            DrawImage(g, curBitmap);
        }

        //private void DrawSymbol(Graphics g, Color color)
        //{
        //    Pen pen = new Pen(color, 3);

        //    InitRect();
        //    //g.DrawRectangle(pen, _rect.X, _rect.Y, _rect.Width, _rect.Height);

        //    PointF[] pArray = new PointF[6];
        //    //Line1
        //    pArray[0] = new PointF(_rect.X, _rect.Y);
        //    pArray[1] = new PointF(_rect.X + _rect.Width, _rect.Y + _rect.Height/3);
        //    //Line2
        //    pArray[2] = new PointF(_rect.X, _rect.Y + _rect.Height / 3);
        //    pArray[3] = new PointF(_rect.X + _rect.Width, _rect.Y);
        //    //Line3
        //    pArray[4] = new PointF(_rect.X + _rect.Width/2, _rect.Y + (_rect.Height / 3)/2);
        //    pArray[5] = new PointF(_rect.X + _rect.Width/2, _rect.Y + _rect.Height);

        //    // Set world transform of graphics object to translate.
        //    Matrix myMatrix = new Matrix();
        //    myMatrix.RotateAt((float)Angle, CenterPoint);
        //    myMatrix.TransformPoints(pArray);
        //    g.DrawLine(pen, pArray[0], pArray[1]);
        //    g.DrawLine(pen, pArray[2], pArray[3]);
        //    g.DrawLine(pen, pArray[4], pArray[5]);

        //    pen.Dispose();
        //}

        /// <summary>
        /// Vẽ các tọa độ gps
        /// </summary>
        /// <param name="g"></param>
        public void Draw(Graphics g)
        {
            //Color color = Color.Blue;
            //DrawSymbol(g, color);
            //DrawImage(g);
            //DrawPlaneImage(g);
            //DrawCircleImage(g);
            DrawCircle(g, Color.Blue);
            //DrawMuiTen(g, color);

        }

        public void DrawRoute(Graphics g, GPSPoint pointTo)
        {
            //Nếu distance nhỏ hơn 50 km thì mới vẽ
            if ((Distance(pointTo)) < 50)
            {
                Pen pen = new Pen(Color.Red, 2);
                g.DrawLine(pen, this.CenterPoint, pointTo.CenterPoint);
                pen.Dispose();
            }
        }

        /// <summary>
        /// Vẽ các tọa độ gps
        /// </summary>
        /// <param name="g"></param>
        public void Draw(Graphics g, Color color)
        {
            //Color color = Color.Blue;
            //DrawSymbol(g, color);
            //DrawImage(g);
            //DrawPlaneImage(g);
            //DrawCircleImage(g);
            DrawCircle(g, color);
            //DrawMuiTen(g, color);

        }

        public void DrawCurrPoint(Graphics g)
        {
            //Color color = Color.Blue;
            //DrawPlaneImage(g);
            DrawArrow2(g, Color.Red);
        }

        public void SaveData()
        {

        }
        /// <summary>
        /// Convert instance to byte array
        /// </summary>
        /// <returns></returns>
        public char[] ConverToByteArray()
        {
            char[] data = new char[GeneralClass.DATA_LEN];
            char[] comma = new char[1] { ',' };
            char[] hemisphereOUmeridienLat;
            int index = 0;

            //name: 4 byte
            char[] nameArray = Name.ToCharArray(0, GeneralClass.NAME_LEN);
            nameArray.CopyTo(data, index);
            index += nameArray.Length;
            //Add comma
            comma.CopyTo(data, index);

            //lat: 8 byte
            index++;
            char[] latArray;
            double value = LatGPS;
            if (value > 0)
                hemisphereOUmeridienLat = new char[1] { 'N' };
            else
                hemisphereOUmeridienLat = new char[1] { 'S' };
            latArray = string.Format("{0}{1}", new string(hemisphereOUmeridienLat), Math.Abs(value).ToString("00.00000").Replace(".", "")).ToCharArray(0, GeneralClass.LAT_LEN);
            latArray.CopyTo(data, index);
            index += latArray.Length;
            //Add comma
            comma.CopyTo(data, index);

            //lon: 9 byte
            index++;
            char[] lonArray;
            value = LonGPS;
            if (LonGPS > 0)
                hemisphereOUmeridienLat = new char[1] { 'E' };
            else
                hemisphereOUmeridienLat = new char[1] { 'W' };
            lonArray = string.Format("{0}{1}", new string(hemisphereOUmeridienLat), Math.Abs(value).ToString("000.00000").Replace(".", "")).ToCharArray(0, GeneralClass.LON_LEN);
            lonArray.CopyTo(data, index);
            index += lonArray.Length;
            //Add comma
            comma.CopyTo(data, index);

            //alt: 4 byte
            index++;
            char[] altArray;
            value = Alt;
            if (value < 10)
                altArray = string.Format("000{0}", value.ToString()).ToCharArray(0, GeneralClass.ALT_LEN);
            else if (value < 100)
                altArray = string.Format("00{0}", value.ToString()).ToCharArray(0, GeneralClass.ALT_LEN);
            else if (value < 1000)
                altArray = string.Format("0{0}", value.ToString()).ToCharArray(0, GeneralClass.ALT_LEN);
            else if (value < 10000)
                altArray = string.Format("{0}", value.ToString()).ToCharArray(0, GeneralClass.ALT_LEN);
            else //error
                altArray = string.Format("-111").ToCharArray();
            altArray.CopyTo(data, index);
            index += altArray.Length;
            //Add comma
            comma.CopyTo(data, index);

            //speed: 3 byte
            index++;
            char[] speedArray;
            value = Speed;
            if (value < 10)
                speedArray = string.Format("00{0}", value.ToString()).ToCharArray(0, GeneralClass.SPEED_LEN);
            else if (value < 100)
                speedArray = string.Format("0{0}", value.ToString()).ToCharArray(0, GeneralClass.SPEED_LEN);
            else if (value < 1000)
                speedArray = string.Format("{0}", value.ToString()).ToCharArray(0, GeneralClass.SPEED_LEN);
            else //error
                speedArray = string.Format("-11").ToCharArray();
            speedArray.CopyTo(data, index);
            index += speedArray.Length;
            //Add comma
            comma.CopyTo(data, index);

            //angle: 3 byte
            index++;
            char[] angleArray;
            value = Angle;
            if (value < 10)
                angleArray = string.Format("00{0}", value.ToString()).ToCharArray(0, GeneralClass.ANGLE_LEN);
            else if (value < 100)
                angleArray = string.Format("0{0}", value.ToString()).ToCharArray(0, GeneralClass.ANGLE_LEN);
            else if (value < 1000)
                angleArray = string.Format("{0}", value.ToString()).ToCharArray(0, GeneralClass.ANGLE_LEN);
            else //error
                angleArray = string.Format("-11").ToCharArray();
            angleArray.CopyTo(data, index);

            return data;
        }

        /// <summary>
        /// Lấy dữ liệu nhận từ cổng COM chuyển thành đối tượng
        /// </summary>
        /// <param name="data"></param>
        public void UpdateFromCOMPort(string data)
        {
            string[] valueArray = data.Split(new char[1] { ',' });
            LocalTime = DateTime.Now;
            Name = valueArray[0];
            string sign;

            sign = valueArray[1].Substring(0, 1);
            double degree = double.Parse(valueArray[1].Substring(1,2));
            double minute = double.Parse(valueArray[1].Substring(3));
            double lat = (minute/60000f) + degree;
            LatGPS = GeneralClass.StringCoordToDecimalCoord(sign + lat.ToString("#.00000"));

            sign = valueArray[2].Substring(0, 1);
            degree = double.Parse(valueArray[2].Substring(1, 3));
            minute = double.Parse(valueArray[2].Substring(4));
            double lon = (minute / 60000f) + degree;
            LonGPS = GeneralClass.StringCoordToDecimalCoord(sign + lon.ToString("#.00000"));

            Alt = Double.Parse(valueArray[3]);
            Speed = Double.Parse(valueArray[4]);
            Angle = Double.Parse(valueArray[5]);

            //InitRect();
        }

        /// <summary>
        /// Hit test.
        /// Return value: -1 - no hit
        ///                0 - hit anywhere
        ///                > 1 - handle number
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
        public int HitTest(PointF point)
        {
            if (Selected)
            {
                for (int i = 1; i <= HandleCount; i++)
                {
                    if (GetHandleRectangle(i).Contains(point))
                        return i;
                }
            }

            if (PointInObject(point))
                return 0;

            return -1;
        }

        /// <summary>
        /// Get handle rectangle by 1-based number
        /// </summary>
        /// <param name="handleNumber"></param>
        /// <returns></returns>
        public RectangleF GetHandleRectangle(int handleNumber)
        {
            PointF point = GetHandle(handleNumber);

            return new RectangleF(point.X - 3, point.Y - 3, 7, 7);
        }

        /// <summary>
        /// Get handle point by 1-based number
        /// </summary>
        /// <param name="handleNumber"></param>
        /// <returns></returns>
        public PointF GetHandle(int handleNumber)
        {
            float x, y, xCenter, yCenter;

            xCenter = _rect.X + _rect.Width / 2;
            yCenter = _rect.Y + _rect.Height / 2;
            x = _rect.X;
            y = _rect.Y;

            switch (handleNumber)
            {
                case 1:
                    x = _rect.X;
                    y = _rect.Y;
                    break;
                case 2:
                    x = xCenter;
                    y = _rect.Y;
                    break;
                case 3:
                    x = _rect.Right;
                    y = _rect.Y;
                    break;
                case 4:
                    x = _rect.Right;
                    y = yCenter;
                    break;
                case 5:
                    x = _rect.Right;
                    y = _rect.Bottom;
                    break;
                case 6:
                    x = xCenter;
                    y = _rect.Bottom;
                    break;
                case 7:
                    x = _rect.X;
                    y = _rect.Bottom;
                    break;
                case 8:
                    x = _rect.X;
                    y = yCenter;
                    break;
            }

            return new PointF(x, y);

        }

        protected bool PointInObject(PointF point)
        {
            return _rect.Contains(point);
        }

        public override bool Equals(System.Object obj)
        {
            // If parameter is null return false.
            if (obj == null)
            {
                return false;
            }

            // If parameter cannot be cast to Point return false.
            GPSPoint p = obj as GPSPoint;
            if ((System.Object)p == null)
            {
                return false;
            }

            // Return true if the fields match:
            return (LocalTime == p.LocalTime) && (LonGPS == p.LonGPS) && (LatGPS == p.LatGPS) && (Alt == p.Alt) && (Speed == p.Speed) && (Angle == p.Angle);
        }

        public bool Equals(GPSPoint p)
        {
            // If parameter is null return false:
            if ((object)p == null)
            {
                return false;
            }

            // Return true if the fields match:
            return (LocalTime == p.LocalTime) && (LonGPS == p.LonGPS) && (LatGPS == p.LatGPS) && (Alt == p.Alt) && (Speed == p.Speed) && (Angle == p.Angle);
        }

        public override int GetHashCode()
        {
            return (int)LocalTime.ToOADate() ^ (int)LonGPS ^ (int)LatGPS ^ (int)Alt ^ (int)Speed ^ (int)Angle;
        }
    }
}
